package server.controller;

import java.util.ArrayList;
import java.util.List;

import database.DBHandler;

import utils.LogHelper;
import forum.*;
import forum.Thread;

public class Controller {
	private ForumsHandler forumsHandler;
	private UsersHandler usersHandler;
	private ArrayList<Viewer> viewers;
	private DBHandler dbHandler;

	public Controller() {
		this.dbHandler = new DBHandler();
		this.forumsHandler = new ForumsHandler(dbHandler);
		this.usersHandler = new UsersHandler(dbHandler);
		this.viewers = new ArrayList<Viewer>();
	}

	public Controller(boolean isWeb) {
		this.dbHandler = new DBHandler();
		this.forumsHandler = new ForumsHandler(dbHandler);
		this.usersHandler = new UsersHandler(dbHandler);
		this.viewers = new ArrayList<Viewer>();
		if (isWeb) {
			this.usersHandler.setWeb(true);
		}
	}

	/**
	 * This method logs user in the forum
	 * 
	 * @param username
	 *            : the user who wishes to log in
	 * @param password
	 *            : the user's password
	 * @return 0 if the log in was successful <br>
	 *         -1 if the login was not successful <br>
	 *         -2 if the user is already logged in
	 */
	public int login(String username, String password) {
		int ans = 0;
		if (!this.usersHandler.login(username, password)) {
			LogHelper.instance().log(
					"Login failed for username " + username);
			return -1;
		}
		LogHelper.instance().log("Login succeed for username " + username);
		return ans;
	}

	/**
	 * This method logs user off the system
	 * 
	 * @param username
	 *            : the name of the user to log off
	 * @return 0 if the logging off was successful <br>
	 *         -1 if the logging off has failed <br>
	 *         -2 if the user is not registered
	 */
	public int logoff(String username) {
		int ans = 0;
		int index = this.usersHandler.findUser(username);
		if (index != -1) {
			if (!this.usersHandler.logoff(username)) {
				LogHelper.instance().log(
						"logoff failed for username " + username);
				ans = -1;
			}
		} else {
			LogHelper.instance().log("logoff failed for username " + username);
			ans = -2;
		}
		if (ans == 0)
			LogHelper.instance().log("logoff succeed for username " + username);
		return ans;
	}

	public synchronized int removeUser(String userToRemove) {

		int ans = this.usersHandler.removeUser(userToRemove);
		if (ans >= 0)
			LogHelper.instance().log(
					"User " + userToRemove + " was removed succesfully");
		else
			LogHelper.instance().log(
					"User " + userToRemove + " was not removed succesfully");
		return ans;
	}

	/**
	 * This method adds new user after the registration of the user into the
	 * forum.
	 * 
	 * @param uName
	 *            : new user's username
	 * @param fName
	 *            : new user's first name
	 * @param lName
	 *            : new user's last name
	 * @param pw
	 *            : new user's password
	 * @param mail
	 *            : new user's e-mail
	 * @param g
	 *            : new user's gender
	 * @return 0 if the adding was successful <br>
	 *         -1 if the physical adding into the list failed <br>
	 *         -2 if user with the same username exists or he is logged in
	 *         already -3 if at least one of the parameters is empty or wrong
	 */
	public synchronized int registerUser(String uName, String fName,
			String lName, String pw, String mail, char g) {
		
		if (uName == null || uName.isEmpty() || fName == null
				|| fName.isEmpty() || lName == null || lName.isEmpty()
				|| pw == null || pw.isEmpty() || mail == null || mail.isEmpty()
				|| (g != 'f' && g != 'm')) {
			LogHelper.instance()
					.log("registerUser " + uName
							+ " failed, because of wrong input");
			return -3;
		}
		int ans = 0;
		int index = this.usersHandler.findUser(uName);
		if (index == -1) {
			Member newUser = new Member(uName, fName, lName, mail,
					Character.toString(g));
			boolean addSuccess = this.usersHandler.registerUser(newUser, pw);
			if (!addSuccess)
				ans = -1;
		} else
			ans = -2;
		if (ans >= 0)
			LogHelper.instance().log("registerUser " + uName + " succeed");
		else
			LogHelper.instance().log("registerUser " + uName + " failed.");

		return ans;
	}

	/**
	 * This method adds friend to user's friends' list.
	 * 
	 * @param username
	 *            : the user who wants to add a friend
	 * @param friendToAdd
	 *            : the friend to add
	 * @return 0 if the adding was successful <br>
	 *         -1 if the adding has failed<br>
	 */
	public synchronized int addUserFriend(String username, String friendToAdd) {
		int ans = 0;
		int index1 = this.usersHandler.findUser(username);
		if (index1 != -1) {
			if (!this.usersHandler.addUserFriend(username, friendToAdd))
				ans = -1;
		}
		if (ans >= 0)
			LogHelper.instance().log("User " + username + " added friend "+friendToAdd+" successfully");
		else
			LogHelper.instance().log("User " + username + " did not add friend "+friendToAdd+" successfully");
		return ans;
	}

	/**
	 * This method removes friend from user's friends' list.
	 * 
	 * @param username
	 *            : the user who wants to remove a friend
	 * @param friendToAdd
	 *            : the friend to add
	 * @return 0 if the removing was successful <br>
	 *         -1 if the removing has failed <br>
	 *         -2 if the user who removes is not logged in
	 */
	public synchronized int removeUserFriend(String username,
			String friendToRemove) {
		int ans = 0;
		boolean loggedIn = this.usersHandler.isUserLoggedin(username);
		if (loggedIn) {
			if (!this.usersHandler.removeUserFriend(username, friendToRemove))
				ans = -1;
		} else
			ans = -2;
		if (ans >= 0)
			LogHelper.instance().log("User " + username + " removed friend "+friendToRemove+" successfully");
		else
			LogHelper.instance().log("User " + username + " did not remove friend "+friendToRemove+" successfully");
		return ans;
	}

	/**
	 * This method adds a new forum with new topic to the list of forums
	 * 
	 * @param subject
	 *            : the subject of new forum
	 * @param usernameWhoAdds
	 *            : user who add the forum with this subject
	 * @return >0 if the adding was successful and id of forum is returned <br>
	 *         -1 if the forum is already exist. <br>
	 *         -2 if the user with given username is not logged in. <br>
	 *         -3 if the user with given username isn't moderator.<br>
	 *         -4 if the user with given username is not registered <br>
	 *         -5 if subject is null or empty
	 */
	public synchronized int addForum(String subject, String usernameWhoAdds) {
		int ans = 0;
		if (!(subject == null || subject.equals(""))) {
			int index = this.usersHandler.findUser(usernameWhoAdds);
			if (index != -1) {
				if (this.usersHandler.isModerator(usernameWhoAdds)) {
					if (this.usersHandler.isUserLoggedin(usernameWhoAdds)) {
						subject = subject.replace("'", "''");
						ans = this.forumsHandler.addForum(subject);
						if (ans == -1)
							ans = -1;
					} else
						ans = -2;
				} else
					ans = -3;
			} else
				ans = -4;
		} else
			ans = -5;
		if (ans >= 0)
			LogHelper.instance().log("New forum with subject "+subject+" was added successfully by "+usernameWhoAdds);
		
		else
			LogHelper.instance().log("User " + usernameWhoAdds + " was not able to add new forum with subject "+subject);
		return ans;
	}

	public synchronized int addForumWeb(String subject) {
		int ans = 0;
		if (!(subject == null || subject.equals(""))) {
			subject = subject.replace("'", "''");
			ans = this.forumsHandler.addForum(subject);
			if (ans == 0) {
				ans = -1;
			}
		} else {
			ans = -1;
		}

		return ans;
	}

	/**
	 * This method adds a new thread to the list of threads in the specified
	 * forum
	 * 
	 * @param forumID
	 *            : the id of forum to which we want to add the thread
	 * @param subject
	 *            : the subject of the new thread
	 * @param content
	 *            : the body of the thread
	 * @param author
	 *            : the user who publishes the new thread
	 * @return 0 if the adding was successful <br>
	 *         -1. if the adding has failed, maybe the thread with this subject
	 *         is already exist, <br>
	 *         -2 if the author is not logged in.<br>
	 *         -3 if the user is not registered<br>
	 *         -4 if subject or content is null or empty
	 */
	public synchronized int addThread(int forumID, String subject,
			String content, String author) {
		int ans = 0;
		if (!(content == null || subject == null || subject.equals("") || content
				.equals(""))) {
			int index = this.usersHandler.findUser(author);
			if (index != -1) {
				if (this.usersHandler.isUserLoggedin(author)) {
					subject = subject.replace("'", "''");
					content = content.replace("'", "''");
					ans = this.forumsHandler.addThread(forumID, subject,
							content, author);
				}
			}
		}
		if (ans >= 0)
			LogHelper.instance().log("User " + author + " added a new thread with this subject "+ subject +".");
		else
			LogHelper.instance().log("User " + author + " was not able to add new thread.");
		return ans;
	}

	/**
	 * This method adds a new post/reply to some thread in the specified forum
	 * 
	 * @param forumID
	 *            : the forum to which thread we add the reply
	 * @param subject
	 *            : the subject of the post
	 * @param content
	 *            : the body of the post
	 * @param author
	 *            : the user who publishes the post
	 * @return 0 if the adding was successful. <br>
	 *         -1 if the adding has failed. <br>
	 *         -2 if the author is not logged in. <br>
	 *         -3 if the author is not registered. <br>
	 *         -4 content or subject is null or empty.
	 */
	public synchronized int addPost(int forumID, int postID, String subject,
			String content, String author) {
		int ans = 0;
		if (!(subject == null || content == null || content.equals("") || subject
				.equals(""))) {
			int index = this.usersHandler.findUser(author);
			if (index != -1) {
				if (this.usersHandler.isUserLoggedin(author)) {
					subject = subject.replace("'", "''");
					content = content.replace("'", "''");
					ans = this.forumsHandler.addReply(forumID, postID, subject,
							content, author);
				}
			}
		}
		if (ans >= 0)
			LogHelper.instance().log("User " + author + " added a new post with subject "+subject+" to thread "+postID);
		else
			LogHelper.instance().log("User " + author + " was not able to add new post.");
		return ans;
	}

	/**
	 * 
	 * @param username
	 * @param forumID
	 * @param postID
	 * @param content
	 * @return 0 on success <br>
	 *         -1 if user has no permeations to edit this post <br>
	 *         or forum/thread not exist <br>
	 *         or such post with new content with same subject and author is
	 *         already exist <br>
	 *         -2 user not logged in <br>
	 *         -3 user does not exist <br>
	 *         -4 content is empty or null
	 */
	public synchronized int editPost(String username, int forumID, int postID,
			String content) {
		int ans = 0;
		Post p = this.getPostByID(postID);
		if (!(p.getAuthor().equals(username) || isUserModerator(username))) {
			return -1;
		}
		if (!(content == null || content.equals(""))) {
			int index = this.usersHandler.findUser(username);
			if (index != -1) {
				if (this.usersHandler.isUserLoggedin(username)) {
					content = content.replace("'", "''");
					if (!this.forumsHandler.editPost(postID, content))
						ans = -1;
				} else
					ans = -2;
			} else
				ans = -3;
		} else
			ans = -4;
		if (ans >= 0)
			LogHelper.instance().log("User " + username + " edited this post: "+postID);
		else
			LogHelper.instance().log("User " + username + " was not able to edit this post: "+postID);
		return ans;
	}

	public synchronized int editPostWeb(int postID, String content) {
		int ans = 0;

		if (!this.forumsHandler.editPost(postID, content))
			ans = -1;

		return ans;
	}

	/**
	 * This method removes the requested post from the requested thread and
	 * forum
	 * 
	 * @param forumID
	 *            : the ID of the forum from which to remove post
	 * @param postID
	 *            : the post to remove
	 * @return 0 if the removing is successful <br>
	 *         -1 if the user is not logged in <br>
	 *         -2 if the user does not have a suitable authority <br>
	 *         -3 if the post does not exist <br>
	 *         -5 if the forum does not exist
	 */
	public synchronized int removePost(String username, int forumID, int postID) {
		int ans = 0;

		Post p = dbHandler.getPost(postID);
		if (p != null) {
			if (this.usersHandler.isModerator(username)
					|| p.getAuthor().equals(username)) {
				if (this.usersHandler.isUserLoggedin(username)) {
					this.forumsHandler.removePost(postID);
				} else
					ans = -1;
			} else
				ans = -2;
		} else
			ans = -3;
		if (ans >= 0)
			LogHelper.instance().log("User " + username + " removed post "+postID+" from forum"+forumID);
		else
			LogHelper.instance().log("User " + username + " did not remove friend "+postID+"from forum "+forumID+" successfully");
		return ans;
	}

	public synchronized int removePostWeb(int postID) {
		int ans = 0;

		Post p = dbHandler.getPost(postID);
		if (p != null) {
			if (!this.forumsHandler.removePost(postID)) {
				ans = -1;
			}
		} else
			ans = -1;
		return ans;
	}

	/**
	 * This method removes a requested thread from the forum with forumID.
	 * 
	 * @param username
	 *            : the user who wishes to remove the thread
	 * @param forumID
	 *            : the forum from which we want to remove the thread
	 * @param rootPostID
	 * @return 0 if the removing was successful <br>
	 *         -1 if the user is not logged in <br>
	 *         -2 if the user does not have the authority for removing or the
	 *         user is not registered. <br>
	 *         -3 if thread does not exist <br>
	 *         -4 if forum does not exist
	 */
	public synchronized int removeThread(String username, int forumID,
			int rootPostID) {
		int ans = 0;
			Thread t = getThreadByID(rootPostID);
			if (t != null) {
				if (this.usersHandler.isModerator(username)
						|| t.getRoot().getAuthor().equals(username)) {
					if (this.usersHandler.isUserLoggedin(username)) {
						this.forumsHandler.removeThread(rootPostID);
					} else
						ans = -1;
				} else
					ans = -1;
			} else
				ans = -1;
		return ans;
	}

	public synchronized int removeThreadWeb(int threadID) {
		int ans = 0;

		if (!this.forumsHandler.removeThread(threadID)) {
			ans = -1;
		}

		return ans;
	}

	/**
	 * This method removes a requested forum from the list of forums.
	 * 
	 * @param username
	 *            : the user who wishes to remove the forum.
	 * @param forumID
	 *            : the forum to remove
	 * @return 0 if the removing was successful. <br>
	 *         -1 if the user is not logged in moderator. <br>
	 *         -2 if the user is not moderator or not registered user. <br>
	 *         -3 if forum does not exist.
	 */
	public synchronized int removeForum(String username, int forumID) {
		int ans = 0;
		Forum f = this.forumsHandler.getForum(forumID);
		if (f != null) {
			if (this.usersHandler.isModerator(username)) {
				if (this.usersHandler.isUserLoggedin(username)) {
					this.forumsHandler.removeForum(forumID);
				} else
					ans = -1;
			} else
				ans = -2;
		} else
			ans = -3;
		if (ans >= 0)
			LogHelper.instance().log("User " + username + " removed forum "+forumID+" successfully");
		else
			LogHelper.instance().log("User " + username + " did not remove forum "+forumID+" successfully");
		return ans;
	}

	public synchronized int removeForumWeb(int forumID) {
		int ans = 0;

		if (!this.forumsHandler.removeForum(forumID)) {
			ans = -1;
		}

		return ans;
	}

	/**
	 * @param forumID
	 *            : the forum we want to show its' threads' topics
	 * @return threads' topics of the specified forum if the user is logged in
	 *         else return an empty string
	 */
	public synchronized List<Thread> getThreadsTopicsOfForum(int forumID) {
		List<Thread> threads = this.dbHandler.getThreads(forumID);
		return threads;
	}

	/**
	 * @param forumID
	 *            : the forum we want to show its' threads
	 * @return threads of the specified forum if the user is logged in else
	 *         returns an empty string
	 */
	public synchronized Thread getThreadsOfTopic(String username, int rootPostID) {
		if (this.usersHandler.isUserLoggedin(username)) {
			// Build thread
			Post rootP = dbHandler.getPost(rootPostID);
			List<Post> replies = dbHandler.getThread(rootPostID);
			rootP.setReplies(replies);
			Thread t = new Thread(rootP);
			return t;
		}
		return null;
	}

	/**
	 * @return the forums
	 */
	public synchronized List<Forum> getForums() {
		return forumsHandler.getForums();
	}

	/**
	 * @param username
	 *            : we want to get back the list of username's friends
	 * @return list of usernames' friends.
	 */
	public synchronized List<Member> getFriendsOfUser(String username) {
		if (this.usersHandler.isUserLoggedin(username))
			return this.usersHandler.getListOfFriends(username);
		return null;
	}

	/**
	 * This method will be useful for the returning liggedInMsg in controller!!!
	 * It will be easy to check if the logged in user is moderator!!!
	 * 
	 * @param username
	 *            : the user to check
	 * @return true or false according to the answer of usersHandler
	 */
	public synchronized boolean isUserModerator(String username) {
		return this.usersHandler.isModerator(username);
	}

	/**
	 * 
	 * @param id
	 *            of the root post
	 * @return thread
	 */
	public Thread getThreadByID(int id) {
		return forumsHandler.getThread(id);
	}

	public Viewer findViewerByName(String uName) {
		if (this.viewers == null) {
			this.viewers = new ArrayList<Viewer>();
		}
		Viewer ans = null;
		for (int i = 0; i < viewers.size(); i++) {
			if (viewers.get(i).getUsername().equals(uName)) {
				ans = viewers.get(i);
			}
		}
		return ans;
	}

	public void removeViewerByName(String uName) {
		if (this.viewers == null) {
			this.viewers = new ArrayList<Viewer>();
		}
		Viewer v = this.findViewerByName(uName);
		if (v != null) {
			this.viewers.remove(v);
		}
	}

	public void addViewerByName(String uName, int tId) {
		if (this.viewers == null) {
			this.viewers = new ArrayList<Viewer>();
		}
		removeViewerByName(uName);
		Viewer v = new Viewer(uName, tId);
		this.viewers.add(v);
	}

	public Post getPostByID(int pId) {
		Post ans = null;
		ans = this.forumsHandler.getPost(pId);
		return ans;
	}

	public List<String> getViewersOfThread(int tId) {
		List<String> ans = new ArrayList<String>();

		for (int i = 0; i < this.viewers.size(); i++) {
			Viewer v = this.viewers.get(i);
			if (v.getThread() == tId) {
				ans.add(v.getUsername());
			}
		}

		return ans;
	}

	public boolean isUserLoggedIn(String username) {
		return this.usersHandler.isUserLoggedin(username);
	}

	public List<Integer> getThreadsIds() {
		return this.forumsHandler.getThreadsIDs();
	}

	public Member getInfoAboutFriend(String friendName) {
		return this.usersHandler.getInfoAboutFriend(friendName);
	}

	public List<Post> getThread(int threadID) {
		return this.usersHandler.getThread(threadID);
	}

	// ////////////////////////TO DELETE AFTER
	// TEST!!!!!!!!!!!!!!!!/////////////////////
	public ArrayList<String> getUsers() {
		return this.usersHandler.getTotalUsers();
	}

}
